﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using NuclexVectorFontContent = Nuclex.Fonts.Content.VectorFontContent;
using NuclexVectorFontCharacterContent = Nuclex.Fonts.Content.VectorFontCharacterContent;
using NuclexOutline = Nuclex.Fonts.Content.VectorFontCharacterContent.Outline;
using NuclexFace = Nuclex.Fonts.Content.VectorFontCharacterContent.Face;

using Microsoft.Xna.Framework;

namespace Qreed.Xna.Font.ContentPipeline
{
    public class VectorFontCharacterContent
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="VectorFontCharacterContent"/> class.
        /// </summary>
        /// <param name="character">The character.</param>
        /// <param name="nuclexCharContent">Content of the nuclex char.</param>
        internal VectorFontCharacterContent(char character, NuclexVectorFontCharacterContent nuclexCharContent)
        {
            _character = character;
            _nuclexCharContent = nuclexCharContent;
        }

        private NuclexVectorFontCharacterContent _nuclexCharContent;
        /// <summary>
        /// Gets the content of the nuclex char.
        /// </summary>
        /// <value>The content of the nuclex char.</value>
        internal NuclexVectorFontCharacterContent NuclexCharContent
        {
            get { return this._nuclexCharContent; }
        }

        private List<VectorFontCharacterOutline> _outlines = new List<VectorFontCharacterOutline>();
        /// <summary>
        /// Gets the outlines.
        /// </summary>
        /// <value>The outlines.</value>
        public List<VectorFontCharacterOutline> Outlines
        {
            get { return this._outlines; }
        }

        private List<VectorFontCharacterTriangle> _triangles = new List<VectorFontCharacterTriangle>();
        /// <summary>
        /// Gets the triangles.
        /// </summary>
        /// <value>The triangles.</value>
        public List<VectorFontCharacterTriangle> Triangles
        {
            get { return this._triangles; }
        }			

        private char _character;
        /// <summary>
        /// Gets the character.
        /// </summary>
        /// <value>The character.</value>
        public char Character
        {
            get { return this._character; }
        }

        private BoundingBox _boundingBox;
        /// <summary>
        /// Gets the bounding box.
        /// </summary>
        /// <value>The bounding box.</value>
        public BoundingBox BoundingBox
        {
            get { return this._boundingBox; }
        }			

        internal void Process()
        {
            ProcessVertices();
            ProcessOutlines();
            ProcessFaces();
        }

        private void ProcessVertices()
        {
            if (NuclexCharContent.Vertices.Count > 0)
            {
                Vector2 min = NuclexCharContent.Vertices[0];
                Vector2 max = NuclexCharContent.Vertices[0];

                for (int i = 1; i < NuclexCharContent.Vertices.Count; i++)
                {
                    Vector2 v = NuclexCharContent.Vertices[i];

                    min.X = Math.Min(min.X, v.X);
                    min.Y = Math.Min(min.Y, v.Y);
                    max.X = Math.Max(max.X, v.X);
                    max.Y = Math.Max(min.Y, v.Y);
                }

                _boundingBox.Min = new Vector3(min, 0.0f);
                _boundingBox.Max = new Vector3(max, 0.0f);
            }
        }

        private void ProcessFaces()
        {
            int faceCount = NuclexCharContent.Faces.Count;

            //if (faceCount == 0 && NuclexCharContent.Vertices.Count > 0)
              //  throw (new Exception("Cannot process faces of character " + this.Character + "."));

            for (int i = 0; i < faceCount; i++)
            {
                NuclexFace face = NuclexCharContent.Faces[i];

                int idx0 = face.FirstVertexIndex;
                int idx1 = face.SecondVertexIndex;
                int idx2 = face.ThirdVertexIndex;

                Vector3 p0 = new Vector3(NuclexCharContent.Vertices[idx0], 0.0f);
                Vector3 p1 = new Vector3(NuclexCharContent.Vertices[idx1], 0.0f);
                Vector3 p2 = new Vector3(NuclexCharContent.Vertices[idx2], 0.0f);

                Vector3 v0 = p1 - p0;
                Vector3 v1 = p2 - p1;

                Vector3 vNormal = Vector3.Cross(v0, v1);

                if (vNormal.Z > 0.0f)
                    Triangles.Add(new VectorFontCharacterTriangle(idx2, idx1, idx0));
                else
                    Triangles.Add(new VectorFontCharacterTriangle(idx0, idx1, idx2));
            }
        }

        private void ProcessOutlines()
        {
            int outlineCount = NuclexCharContent.Outlines.Count;

            for (int i = 0; i < outlineCount; ++i)
            {
                NuclexOutline nuclexOutline = NuclexCharContent.Outlines[i];
                bool isInner = false;

                for (int j = 0; j < outlineCount; j++)
                {
                    if (i == j)
                        continue;

                    NuclexOutline compareWith = NuclexCharContent.Outlines[j];

                    if (IsInnerOutline(nuclexOutline, compareWith, NuclexCharContent.Vertices))
                    {
                        isInner = true;
                        break;
                    }
                }

                VectorFontCharacterOutline outline = new VectorFontCharacterOutline(nuclexOutline.StartVertexIndex, nuclexOutline.VertexCount, isInner);
                Outlines.Add(outline);
            }
        }

        private bool IsInnerOutline(NuclexOutline nuclexOutline, NuclexOutline compareWith, List<Vector2> vertices)
        {
            bool isInner = false;

            for (int i = 0; i < nuclexOutline.VertexCount; i++)
            {
                Vector2 p = vertices[nuclexOutline.StartVertexIndex + i];

                if (Pnpoly(compareWith, p.X, p.Y, vertices))
                {
                    isInner = true;
                    break;
                }
            }

            return isInner;
        }

        private bool Pnpoly(NuclexOutline compareWith, float testx, float testy, List<Vector2> vertices)
        {
            // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
            bool c = false;

            int i, j;

            for (i = 0, j = compareWith.VertexCount - 1; i < compareWith.VertexCount; j = i++)
            {
                float vertx_i = vertices[compareWith.StartVertexIndex + i].X;
                float verty_i = vertices[compareWith.StartVertexIndex + i].Y;
                float vertx_j = vertices[compareWith.StartVertexIndex + j].X;
                float verty_j = vertices[compareWith.StartVertexIndex + j].Y;

                if (((verty_i > testy) != (verty_j > testy)) &&
                    (testx < (vertx_j - vertx_i) * (testy - verty_i) / (verty_j - verty_i) + vertx_i))
                {
                    c = !c;
                }
            }

            return c;
        }
    }
}
